package gridmath;

import java.util.HashMap;

import preprocessing.WindowingSystem;


public class CaseMatrixBase extends AbstractCaseMatrix {
	
	protected HashMap<MatrixOperations, Boolean> typesOfOperators = new HashMap<MatrixOperations, Boolean>();
	protected MatrixOperations specialTypeOfOperation;
	protected MatrixOperations specialTypeOfOperand;
	
	
	public CaseMatrixBase(WindowingSystem windowmaker, int maxD) {
		super(windowmaker, maxD);
		
		matrix1D = new double [noOfWindows];
		if(maxDimension > 1)
			matrix2D = new double [noOfWindows][noOfWindows];
		if(maxDimension > 2)
			matrix3D = new double [noOfWindows][noOfWindows][noOfWindows];
		if(maxDimension > 3)
			matrix4D = new double [noOfWindows][noOfWindows][noOfWindows][noOfWindows];
		
		resetIndices();
	
		typesOfOperators.put(MatrixOperations.greaterThan, true);
		typesOfOperators.put(MatrixOperations.lessThan, true);
		typesOfOperators.put(MatrixOperations.notEqZeroMask, true);
		typesOfOperators.put(MatrixOperations.copy, true);
		typesOfOperators.put(MatrixOperations.matrixMultiply, true);
		typesOfOperators.put(MatrixOperations.Exclusive, true);
		typesOfOperators.put(MatrixOperations.Union, true);
		
		specialTypeOfOperation = null;
		specialTypeOfOperand = null;
	}


	public void resetIndices() {
		i=0; j=0; k=0; m=0; currentDimension = 0;
	}

	public CaseMatrixBase (WindowingSystem windowmaker, int maxD, double initialValue) {
		this(windowmaker, maxD);
		
		for(i=0; i<noOfWindows; i++) {
			matrix1D[i] = initialValue;
			
			if(maxDimension > 1) {
			for(j=0; j<noOfWindows; j++) {
				matrix2D[i][j] = initialValue;
				
				if(maxDimension > 2) {
				for(k=0; k<noOfWindows; k++) {
					matrix3D[i][j][k] = initialValue;
					
					if(maxDimension > 3) {
					for(m=0; m<noOfWindows; m++) {
						matrix4D[i][j][k][m] = initialValue;
					} }
				} }
			} }
		}
						
	}
	
	public CaseMatrixBase compute (MatrixOperations operationType, CaseMatrixBase argumentMatrices, boolean inPlace) {
		
		if ( 	(! this.typesOfOperators.containsKey (operationType)) 
				&& (argumentMatrices != null) 
				&& (specialTypeOfOperand != argumentMatrices.specialTypeOfOperation) ) 
		{
				System.err.println("Incompatible operation " + operationType + " requested from object.");
				System.exit(1);
		}
		
		
		CaseMatrixBase result = this; //default inPlace multiplication
		if( ! inPlace ) {
			result = new CaseMatrixBase(windowMaker, maxDimension).compute(MatrixOperations.copy, this, true);
			result.specialTypeOfOperation = specialTypeOfOperation;
			result.specialTypeOfOperand = specialTypeOfOperand;
		}
		
		if(argumentMatrices != null) {
//			result.calculateMatrix1D (argumentMatrices.getMatrix1D(), operationType);
			if(maxDimension > 1)
				result.calculateMatrix2D (argumentMatrices.getMatrix2D(), operationType);
//			if(maxDimension > 2)
//				result.calculateMatrix3D (argumentMatrices.getMatrix3D(), operationType);
//			if(maxDimension > 3)
//				result.calculateMatrix4D (argumentMatrices.getMatrix4D(), operationType);
		}
		else {
//			result.calculateMatrix1D (null, operationType);
			if(maxDimension > 1)
				result.calculateMatrix2D (null, operationType);
//			if(maxDimension > 2)
//			result.calculateMatrix3D (null, operationType);
//			if(maxDimension > 3)
//				result.calculateMatrix4D (null, operationType);
		}
			
		resetIndices();
		return result;
	}
	
	
	public double doOperation (double operand) {
		System.err.println("Cannot perform complex/unspecified operation on this object");
		return operand;
	}
	
	
	public double doOperation (double operand, MatrixOperations operation) {
		
		if(operation == MatrixOperations.greaterThan) 
			return greaterThan(operand);
		
		if(operation == MatrixOperations.lessThan)
			return lessThan(operand);
		
		if(operation == MatrixOperations.notEqZeroMask)
			return notEqZeroMask(operand);
		
		if(operation == MatrixOperations.copy)
			return copy(operand);
		
		if(operation == MatrixOperations.matrixMultiply)
			return matrixMultiply(operand);
		
		if(operation == MatrixOperations.Exclusive)
			return exclusive(operand);
		
		if(operation == MatrixOperations.Union)
			return union(operand);
		
		//default 
		return doOperation(operand);
	}

	
	public double greaterThan (double operand) {
		switch(currentDimension) {
		case 1:
			if (matrix1D[i] >= operand ) 
				return matrix1D[i];
			//System.out.println(i);
			return 0;
			
		case 2:
			if (matrix2D[i][j] >= operand )
				return matrix2D[i][j]; 
			//System.out.println(i+","+j);
			return 0;
			
		case 3:
			if ( matrix3D[i][j][k] >= operand ) 
				return matrix3D[i][j][k];
			//System.out.println(i+","+j+","+k);
			return 0;
			
		default : 
			if ( matrix4D[i][j][k][m] >= operand )
				return matrix4D[i][j][k][m];
			//System.out.println(i+","+j+","+k+","+m);
			return 0;
		}
	}
	
	
	public double lessThan (double operand) {
		switch(currentDimension) {
		case 1:
			if ( matrix1D[i] <= operand ) 
				return matrix1D[i];
			//System.out.println(i);
			return 0;
			
		case 2:
			if ( matrix2D[i][j] <= operand )
				return matrix2D[i][j]; 
			//System.out.println(i+","+j);
			return 0;
			
		case 3:
			if ( matrix3D[i][j][k] <= operand ) 
				return matrix3D[i][j][k];
			//System.out.println(i+","+j+","+k);
			return 0;
			
		default : 
			if ( matrix4D[i][j][k][m] <= operand )
				return matrix4D[i][j][k][m];
			//System.out.println(i+","+j+","+k+","+m);
			return 0;
		}
		
	}
	
	public double notEqZeroMask (double operand) {	//null operand
		switch(currentDimension) {
		case 1:
			return ( matrix1D[i] != 0 ) ? 1 : 0;
		case 2:
			return ( matrix2D[i][j] != 0 ) ? 1 : 0;
		case 3:
			return ( matrix3D[i][j][k] != 0 ) ? 1 : 0;
		default : 
			return ( matrix4D[i][j][k][m] != 0 ) ? 1 : 0;
		}
	}
	
	public double copy(double operand) {
		return operand;
	}

	public double matrixMultiply(double operand) {
		switch(currentDimension) {
			case 1:
				return matrix1D[i] * operand;
			case 2:
				return matrix2D[i][j] * operand;
			case 3: 
				return matrix3D[i][j][k] * operand;
			default:
				return matrix4D[i][j][k][m] * operand;
		}
	}
	
	public double exclusive(double operand) {
		switch(currentDimension) {
		case 1:
			return (operand == 0 ) ? matrix1D[i] : 0;
		case 2:
			return (operand == 0 ) ? matrix2D[i][j] : 0;
		case 3: 
			return (operand == 0 ) ? matrix3D[i][j][k] : 0;
		default:
			return (operand == 0 ) ? matrix4D[i][j][k][m] : 0;
		}
	}
	
	
	public double union(double operand) {
		switch(currentDimension) {
		case 1:
			return ( matrix1D[i] == 0 ) ? operand : matrix1D[i];
		case 2:
			return ( matrix2D[i][j] == 0 ) ? operand : matrix2D[i][j];
		case 3: 
			return ( matrix3D[i][j][k] == 0 ) ? operand : matrix3D[i][j][k];
		default:
			return ( matrix4D[i][j][k][m] == 0 ) ? operand : matrix4D[i][j][k][m];
		}
	}
	
}
